package com.axonactive.yolo.service.automatic.search;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.mail.MessagingException;

import org.apache.log4j.Logger;
import org.atmosphere.cpr.BroadcasterFactory;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;

import com.axonactive.yolo.domain.Task;
import com.axonactive.yolo.domain.TaskNotification;
import com.axonactive.yolo.domain.TaskUserId;
import com.axonactive.yolo.dto.Email;
import com.axonactive.yolo.dto.Flight;
import com.axonactive.yolo.dto.Parameters;
import com.axonactive.yolo.dto.TaskAlert;
import com.axonactive.yolo.dto.TaskNotificationDTO;
import com.axonactive.yolo.exception.FlightNotificationNotFoundException;
import com.axonactive.yolo.exception.TaskNotFoundException;
import com.axonactive.yolo.monitor.ServiceProvider;
import com.axonactive.yolo.exception.TaskNotificationNotFoundException;
import com.axonactive.yolo.notification.FlightNotifier;
import com.axonactive.yolo.notification.TwilioSMSService;
import com.axonactive.yolo.service.interfaces.EmailService;
import com.axonactive.yolo.service.interfaces.ExchangeRateService;
import com.axonactive.yolo.service.interfaces.TaskService;
import com.axonactive.yolo.service.interfaces.TaskUserIdService;
import com.axonactive.yolo.service.interfaces.UserService;
import com.axonactive.yolo.service.internal.FlightFactoryService;
import com.axonactive.yolo.service.internal.FlightRequesterService;
import com.axonactive.yolo.service.internal.TaskNotificationService;
import com.axonactive.yolo.util.CONSTANTS;

/**
 * For get tasks available
 * 
 */
public class TaskPrepartion {

	@Autowired
	private TaskService taskService;

	@Autowired
	private TaskUserIdService taskUserIdService;

	@Autowired
	private ExchangeRateService exchangeRateService;

	@Autowired
	private EmailService emailService;

	@Autowired
	private UserService userService;
	
	@Autowired
	private TaskNotificationService taskNotificationService;

	private static final Logger LOGGER = Logger.getLogger(TaskPrepartion.class);

	private void injectService() {
		ServiceProvider serviceProvider = ServiceProvider.getInstance();
		serviceProvider.injectService(taskService);
		serviceProvider.injectService(exchangeRateService);
		serviceProvider.injectService(taskUserIdService);
		serviceProvider.injectService(userService);
	}
	
	public void run() {
		injectService();
		
		try {
			executeAutomaticSearch();
		} catch (ParseException e) {
			e.printStackTrace();
		}
	}
	
	public void runManually(TaskService taskService, TaskUserIdService taskUserIdService,
			ExchangeRateService exchangeRateService, EmailService emailService,
			UserService userService, TaskNotificationService taskNotificationService) {
		this.taskService = taskService;
		this.taskUserIdService = taskUserIdService;
		this.exchangeRateService = exchangeRateService;
		this.emailService = emailService;
		this.userService = userService;
		this.taskNotificationService = taskNotificationService;
		ServiceProvider serviceProvider = ServiceProvider.getInstance();
		serviceProvider.injectService(exchangeRateService);
		
		try {
			executeAutomaticSearch();
		} catch (ParseException e) {
			e.printStackTrace();
		}
	}

	public void executeAutomaticSearch() throws ParseException {
		FlightFactoryService flightFactoryService=new FlightFactoryService();
		
		this.updateTaskStatus();
		Map<Task, List<TaskUserId>> availableTasks = this.getAvailableTask();
		FlightRequesterService flightRequesterService = new FlightRequesterService();

		List<Task> tasks = new ArrayList<>(availableTasks.keySet());

		for (int i = 0; i < tasks.size(); i++) {
			Task task = tasks.get(i);
			List<TaskUserId> taskUserIds = availableTasks.get(task);

			Parameters param = Parameters.getParamFromTask(task);

			LOGGER.info("Searching for task: " + task.getFromCode() + " - " + task.getToCode() + ", " + task.getDepartDate().toLocaleString());
			List<Flight> flights = flightRequesterService.getInstance().sendRequestForTask(param);
			LOGGER.info("Search completed: " + flights.size());

			for (TaskUserId taskUserId : taskUserIds) {
				LOGGER.info("size = " + flights.size());
				List<Flight> userFlights = new ArrayList<Flight>(flights.size());
				for (Flight flight : flights) {
					userFlights.add(new Flight(flight));
				}

				LOGGER.info("Checking for userId: " + taskUserId.getId());

				// convert, check price
				flightFactoryService.addToRepository(userFlights);
				userFlights=flightFactoryService.convertAndFilterAmountByInputCurrency(taskUserId.getCurrencyCode(), taskUserId.getMaxPrice());
				flightFactoryService.clearRepository();
				
				// sort
				sortFlightsbyPrice(userFlights);
				
				int num_results = (CONSTANTS.TASK_NUM_RESULTS <= userFlights.size() ? CONSTANTS.TASK_NUM_RESULTS : userFlights.size());
				userFlights = userFlights.subList(0, num_results);

				for (int j = 0; j < userFlights.size(); j++) {
					Flight flight = userFlights.get(j);
					LOGGER.info("Result " + j + ": " + flight.getOutBound().getDepartAirports().get(0).getAirline().getFlightNumber() + ", " + flight.getPrice().getAmount() + " " + flight.getPrice().getCurrencyCode());
				}

				//send notification
				sendNotificationToUser(taskUserId, userFlights);
				
				LOGGER.info("Finish automatic search");
			}
		}
	}

	private void sortFlightsbyPrice(List<Flight> flights) {
		for (int i = 0; i < flights.size() - 1; i++)
			for (int j = i + 1; j < flights.size(); j++) {
				Flight fi = flights.get(i);
				Flight fj = flights.get(j);
				if (Double.parseDouble(fj.getPrice().getAmount()) < Double.parseDouble(fi.getPrice().getAmount())) {
					Flight temp = fi;
					flights.set(i, fj);
					flights.set(j, temp);
				}
			}
	}

	private void updateTaskStatus() {
		List<Task> allTask = taskService.findAll();

		for (int i = 0; i < allTask.size(); i++) {
			if (new Date().compareTo(allTask.get(i).getDepartDate()) >= 0) {
				Task tasks = taskService.findById(allTask.get(i).getId());
				tasks.setAvailable("0");
				LOGGER.info("Task is unavailable: " + tasks.getTaskId());
				try {
					taskService.update(tasks);
				} catch (TaskNotFoundException e) {
					e.printStackTrace();
				}
			}

		}
	}

	public Map<Task, List<TaskUserId>> getAvailableTask() {
		Map<Task, List<TaskUserId>> availableTasks = new java.util.Hashtable<Task, List<TaskUserId>>();
		List<Task> ta = taskService.findByAvailable("1");
		LOGGER.info("We have : " + ta.size() + " is available");

		for (int i = 0; i < ta.size(); i++) {
			List<TaskUserId> taskUserIds = new ArrayList<TaskUserId>();
			taskUserIds = taskUserIdService.findByTaskId(ta.get(i).getTaskId());

			availableTasks.put(ta.get(i), taskUserIds);
		}

		return availableTasks;
	}

//	public void injectService(TaskService taskService, TaskUserIdService taskUserIdService) {
//		this.taskService = taskService;
//		this.taskUserIdService = taskUserIdService;
//
//	}

	/**
	 * Send email, noty, sms to user
	 * 
	 * @param userId
	 * @param flights
	 *            new 5 flights
	 */
	private void sendNotificationToUser(TaskUserId taskUserId, List<Flight> flights) {
		if (taskUserId.getUserId() != -1 && flights != null && flights.size() != 0) {

			// send to notification center
			List<TaskNotification> taskNotifications = new ArrayList<>();
			TaskNotification taskNotification = new TaskNotification();
			taskNotification.setUserId(taskUserId.getUserId());
			taskNotification.setTaskId(taskUserId.getTaskId());
			taskNotification.setTaskName(taskUserId.getName());
			
			String msg = "";
			for (int i = 0; i < flights.size() && i <= 5; i++) {
				msg += flights.get(i).getOutBound().getDepartAirports().get(0).getAirline().getFlightNumber() + " - "
						+ flights.get(i).getPrice().getAmount() + flights.get(i).getPrice().getCurrencyCode() + "; ";
			}
			msg = msg.substring(0, msg.length() - 2);
			
			taskNotification.setTaskInfo(msg);
			
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			Date now = new Date();
			taskNotification.setSaveTime(sdf.format(now));
			taskNotification.setIsRead(0);

			if (BroadcasterFactory.getDefault().lookup(taskUserId.getUserId()) != null) {
				// user is online
				taskNotification.setStatus(1);
				// for get ID to update later
				taskNotification = taskNotificationService.create(taskNotification);
				taskNotifications.add(taskNotification);
				
				List<TaskNotificationDTO> taskNotificationDTOs = new ArrayList<>();
				taskNotificationDTOs = taskNotificationService.convertFromTaskNotification(taskNotifications);
				
				ObjectMapper objectMapper = new ObjectMapper();
				String json = "";
				try {
					json = objectMapper.writeValueAsString(taskNotificationDTOs);
					json = "11" + json;

					if (!BroadcasterFactory.getDefault().lookup(taskUserId.getUserId())
							.broadcast(json).isDone()) {
						// send task notification error
						taskNotification.setStatus(0);
						try {
							taskNotificationService.update(taskNotification);
						} catch (TaskNotificationNotFoundException e) {
							LOGGER.error("database error", e);
						}
					}
				} catch (JsonGenerationException e) {
					LOGGER.error("json error", e);
				} catch (JsonMappingException e) {
					LOGGER.error("json error", e);
				} catch (IOException e) {
					LOGGER.error("io error", e);
				}
			}
			else {
				//user is not online
				taskNotification.setStatus(0);
				taskNotificationService.create(taskNotification);
			}
			
			// send email
			boolean isRoundtrip = false;
			if (flights.get(0).getInBound() != null) {
				isRoundtrip = true;
			}
			List<TaskAlert> taskAlerts = Flight.convertToTaskAlert(flights, isRoundtrip);

			Email email = new Email();
			if (isRoundtrip) {
				email = emailService.setRoundTripTaskEmailTemplate("tdtrinhsiho@gmail.com", taskAlerts, "Alert Email - Flight Information Found", "alert-task-roundtrip");
			} else {
				email = emailService.setRoundTripTaskEmailTemplate("tdtrinhsiho@gmail.com", taskAlerts, "Alert Email - Flight Information Found", "alert-task-oneway");
			}

			try {
				emailService.sendEmail_(email);
			} catch (MessagingException e) {
				LOGGER.error("email can't be sent", e);
			}
			
			// send sms message
			FlightNotifier.getInstance().sendSMSForTask(flights, userService.findById(taskUserId.getUserId()), taskUserId);
		}

	}

}
